<style>
  @keyframes keyF1 {
    0% {
      background-color: rgb(123, 243, 0);
      transform: rotate(0);
      width: 80px;
    }

    50% {
      background-color: rgb(183, 243, 255);
      transform: rotate(180deg);
      width: 120px;
    }

    100% {
      background-color: rgb(123, 243, 0);
      transform: rotate(360deg);
      width: 80px;
    }
  }
  .wrapper {
    display: grid;
    grid-template-columns: repeat(5, minmax(130px, 300px));
    grid-template-rows: repeat(2, 150px);
  }

  .card {
    height: 100px;
    width: 100px;
    align-self: center;
    justify-self: center;
    animation: keyF1 4s linear infinite;
  }

  .tip {
    padding: 0 16px 18px;
  }
</style>
<div class="wrapper">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>

<div class="tip">
  <button onclick="test1()">test1</button><span id="test1"> look console ！</span>
  <p>micro的优先级始终最高，一直执行，直到没有了，才执行macro和raf（css动画时基于raf的，所以css动画也会卡住）</p>
  <p>每个帧可能执行多个macro，但只会执行一个raf</p>
  <p>通常优先执行一个或多个macro，但如果micro占用了过多的时间，可能没有机会执行macro，直接执行raf</p>
  <br />
  <p>raf之后就是渲染过程了，涉及：</p>
  <ol>
    <li>
      parse
      html：解析html，css，js，更新DOMTree（用户可能调用document.write，并不是直接操作DOMTree，所以每一帧还是要有这个过程）
    </li>
    <li>recalc styles：计算样式，更新CSSTree</li>
    <li>
      layout：由DOMTree和CSSTree计算生成
      LayerTree，一个DOM节点可能对应多个LayerTree节点，比如audio元素，隐藏DOM不会产生对应的节点等。
    </li>
    <li>Update Layer Tree：产生层叠上下文</li>
    <li>repaint：形成一个层级记录表，记录图层绘制的先后顺序</li>
    <li>
      composition：layerTree和层级记录表将页面分成多个图块，提交给编排线程。即使页面只有一个层级，页面过长时也会被拆成多个图块，视口需要展示的内容涉及哪些图块也是在这里计算得到的
    </li>
    <li>raster：栅格化数据交给GPU绘制</li>
  </ol>

  <p>
    渲染过程之后就是requestIdleCallback了。一次渲染要花多长时间呢？<button onclick="test2()">test2</button> look
    console!
  </p>
</div>

<script>
  function wait(time) {
    const t = Date.now();
    while (Date.now() - t < time);
  }
  const BLOCK_TIME = 3;

  function test1(num = 500) {
    function micro(restCount) {
      Promise.resolve().then(() => {
        wait(BLOCK_TIME);
        console.log('---- micro --');
        if (restCount > 1) {
          micro(restCount - 1);
        }
      });
    }
    function macro(restCount) {
      setTimeout(() => {
        wait(BLOCK_TIME);
        console.log('///// macro --');
        if (restCount > 1) {
          macro(restCount - 1);
        }
      }, 0);
    }
    function raf(restCount) {
      requestAnimationFrame(() => {
        wait(BLOCK_TIME);
        console.log('==== raf!! ===');
        if (restCount > 1) {
          raf(restCount - 1);
        }
      });
    }

    raf(num);
    macro(num);
    micro(num);
  }

  function test2(num = 60) {
    // let start = 0
    function ric(restCount) {
      requestIdleCallback(() => {
        console.timeEnd();
        // let end = Date.now()
        // console.log('--- ric ---', end - start)
        // start = end
        if (restCount > 1) {
          ric(restCount - 1);
        }
      });
    }
    function raf(restCount) {
      requestAnimationFrame(() => {
        start = Date.now();
        console.time();
        // console.log('--- raf ---')
        if (restCount > 1) {
          raf(restCount - 1);
        }
      });
    }
    raf(num);
    ric(num);
  }
</script>
